package com.example.demo.ykj.rainfall;

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.read.listener.ReadListener;
import com.alibaba.excel.util.ListUtils;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.alibaba.fastjson.JSON;
import com.example.demo.ykj.rainfall.entity.Rainfall;
import com.example.demo.ykj.rainfall.pojo.ExportRainfall;
import com.example.demo.ykj.rainfall.repository.RainfallRepository;
import com.example.demo.ykj.rainfall.repository.RainfallSaveService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author zjq
 * @version 创建时间：2022-07-04 15:24:52
 */

@Api(tags = {"余康俊的数据"})
@RestController
@RequestMapping("/ykj/rainfall")
@Slf4j
public class RainfallController {

    @Autowired
    private RainfallSaveService rainfallSaveService;

    /**
     * 目前是最快的速度
     * 1.数据库配置文件开启&rewriteBatchedStatements=true批量保存
     * 2.jpa的主键政策不能是自增---目前使用jpa-uuid
     * jpa自带的saveAll方法一条一条执行需要先查询，再保存
     *
     * @param file
     * @throws IOException
     */
    @ApiOperation(value = "水利雨量数据写入数据库")
    @PostMapping("import")
    @ResponseBody
    public void uploadFileData(MultipartFile file) throws IOException {
        Long start = System.currentTimeMillis();

        //写法1 2速度差不多

        //// 写法1：JDK8+ ,不用额外写一个DemoDataListener
        //// since: 3.0.0-beta1
        //EasyExcel.read(file.getInputStream(), Rainfall.class, new PageReadListener<Rainfall>(dataList -> {
        //    // 这里 需要指定读用哪个class去读，然后读取第一个sheet 文件流会自动关闭
        //    // 这里每次会读取100条数据 然后返回过来 直接调用使用数据就行
        //    //for (Rainfall demoData : dataList) {
        //    //    //System.out.println("读取到一条数据{}" + JSON.toJSONString(demoData));
        //    //    rainfallSaveService.save(demoData);
        //    //}
        //    rainfallRepository.batchSave(dataList);
        //})).sheet().doRead();

        // 写法2：
        // 匿名内部类 不用额外写一个DemoDataListener
        // 这里 需要指定读用哪个class去读，然后读取第一个sheet 文件流会自动关闭
        EasyExcel.read(file.getInputStream(), Rainfall.class, new ReadListener<Rainfall>() {
            /**
             * 单次缓存的数据量
             */
            public static final int BATCH_COUNT = 10000;
            /**
             *临时存储
             */
            private List<Rainfall> cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);

            @Override
            public void invoke(Rainfall data, AnalysisContext context) {
                cachedDataList.add(data);
                if (cachedDataList.size() >= BATCH_COUNT) {
                    saveData();
                    // 存储完成清理 list
                    cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
                }
            }

            @Override
            public void doAfterAllAnalysed(AnalysisContext context) {
                saveData();
            }

            /**
             * 加上存储数据库
             */
            private void saveData() {
                log.info("{}条数据，开始存储数据库！", cachedDataList.size());

                //两者保存速度差不多---均为批量保存
                //rainfallSaveService.batchSave(cachedDataList);
                rainfallRepository.batchSave(cachedDataList);
                log.info("存储数据库成功！");
            }
        }).sheet().doRead();

        Long end = System.currentTimeMillis();
        System.out.println("开始时间" + start);
        System.out.println("结束时间" + end);
        Long totaltime = end - start;
        System.out.println("花费时间" + totaltime);

        //76053条数据----读取速度很快，批量保存的速度比较慢,解决保存速度问题
        // PageReadListener 单次缓存100
        //开始时间 11:13:54 结束时间 11:15:47   小于2min  一次存储1k条数据  76053条数据
        //开始时间 11:23:36 结束时间 11:25:21   小于2min  一次存储5k条数据  76053条数据
        //开始时间 11:19:37 结束时间 11:21:44   大于2min  一次存储10k条数据  76053条数据

        //匿名内部类
        //1分45秒  单次读取10k 一次最多存储100k条数据
        //1分34秒  单次读取10k 一次最多存储5k条数据 15:07:46 15:09:20

        //批量保存---69648条数据
        //有&rewriteBatchedStatements=true
        //druid监控执行数140次   12090ms
        //配置文件没写&rewriteBatchedStatements=true
        //druid监控执行数140次   66182ms


    }

    //导出处理之后的excel
    @Autowired
    private RainfallRepository rainfallRepository;

    @ApiOperation(value = "水利雨量统计数据（包含两个模块）---用这个")
    @GetMapping("exportData")
    public void exportData(HttpServletResponse response) throws IOException {
        //第一个Sheet的数据
        List<ExportRainfall> list = new ArrayList<ExportRainfall>();
        //这里可以插入一些默认数据
        List<Map<String, Object>> a = rainfallRepository.getHour();
        for (int i = 0; i < a.size(); i++) {
            ExportRainfall b = new ExportRainfall();
            b.setDay(a.get(i).get("day") + "");
            b.setTotal(a.get(i).get("total") + "");
            list.add(b);
        }

        //第二个Sheet的数据
        List<ExportRainfall> list1 = new ArrayList<ExportRainfall>();
        //这里可以插入一些默认数据
        List<Map<String, Object>> a1 = rainfallRepository.getDay();
        for (int i = 0; i < a1.size(); i++) {
            ExportRainfall b1 = new ExportRainfall();
            b1.setDay(a1.get(i).get("day") + "");
            b1.setTotal(a1.get(i).get("total") + "");
            list1.add(b1);
        }

        // 这里注意 有同学反应使用swagger 会导致各种问题，请直接用浏览器或者用postman
        try {
            response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
            response.setCharacterEncoding("utf-8");
            String fileName = URLEncoder.encode("测试", "UTF-8").replaceAll("\\+", "%20");
            response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");
            // 这里需要设置不关闭流
            //EasyExcel.write(response.getOutputStream(), ExportRainfall.class).autoCloseStream(Boolean.FALSE).sheet("模板")
            //        .doWrite(list);

            // 这里 指定文件
            try (ExcelWriter excelWriter = EasyExcel.write(response.getOutputStream(), ExportRainfall.class).build()) {
                // 每次都要创建writeSheet 这里注意必须指定sheetNo 而且sheetName必须不一样
                WriteSheet writeSheet = EasyExcel.writerSheet("统计小时").build();
                // 分页去数据库查询数据 这里可以去数据库查询每一页的数据
                excelWriter.write(list, writeSheet);

                WriteSheet writeSheet1 = EasyExcel.writerSheet("统计日").build();
                // 分页去数据库查询数据 这里可以去数据库查询每一页的数据
                excelWriter.write(list1, writeSheet1);
            }

        } catch (Exception e) {
            // 重置response
            response.reset();
            response.setContentType("application/json");
            response.setCharacterEncoding("utf-8");
            Map<String, String> map = new HashMap<String, String>();
            map.put("status", "failure");
            map.put("message", "下载文件失败" + e.getMessage());
            response.getWriter().println(JSON.toJSONString(map));
        }
    }


    @ApiOperation(value = "水利雨量小时统计数据导出")
    @GetMapping("exportHourData")
    public void exportHourData(HttpServletResponse response) throws IOException {
        List<ExportRainfall> list = new ArrayList<ExportRainfall>();
        //这里可以插入一些默认数据
        List<Map<String, Object>> a = rainfallRepository.getHour();
        for (int i = 0; i < a.size(); i++) {
            ExportRainfall b = new ExportRainfall();
            b.setDay(a.get(i).get("day") + "");
            b.setTotal(a.get(i).get("total") + "");
            list.add(b);
        }

        // 这里注意 有同学反应使用swagger 会导致各种问题，请直接用浏览器或者用postman
        try {
            response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
            response.setCharacterEncoding("utf-8");
            String fileName = URLEncoder.encode("测试", "UTF-8").replaceAll("\\+", "%20");
            response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");
            // 这里需要设置不关闭流
            EasyExcel.write(response.getOutputStream(), ExportRainfall.class).autoCloseStream(Boolean.FALSE).sheet("模板")
                    .doWrite(list);
        } catch (Exception e) {
            // 重置response
            response.reset();
            response.setContentType("application/json");
            response.setCharacterEncoding("utf-8");
            Map<String, String> map = new HashMap<String, String>();
            map.put("status", "failure");
            map.put("message", "下载文件失败" + e.getMessage());
            response.getWriter().println(JSON.toJSONString(map));
        }
    }

    @ApiOperation(value = "水利雨量天数统计数据导出")
    @GetMapping("exportDayData")
    public void exportDayData(HttpServletResponse response) throws IOException {
        List<ExportRainfall> list = new ArrayList<ExportRainfall>();
        //这里可以插入一些默认数据
        List<Map<String, Object>> a = rainfallRepository.getDay();
        for (int i = 0; i < a.size(); i++) {
            ExportRainfall b = new ExportRainfall();
            b.setDay(a.get(i).get("day") + "");
            b.setTotal(a.get(i).get("total") + "");
            list.add(b);
        }

        // 这里注意 有同学反应使用swagger 会导致各种问题，请直接用浏览器或者用postman
        try {
            response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
            response.setCharacterEncoding("utf-8");
            String fileName = URLEncoder.encode("测试", "UTF-8").replaceAll("\\+", "%20");
            response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");
            // 这里需要设置不关闭流
            EasyExcel.write(response.getOutputStream(), ExportRainfall.class).autoCloseStream(Boolean.FALSE).sheet("模板")
                    .doWrite(list);
        } catch (Exception e) {
            // 重置response
            response.reset();
            response.setContentType("application/json");
            response.setCharacterEncoding("utf-8");
            Map<String, String> map = new HashMap<String, String>();
            map.put("status", "failure");
            map.put("message", "下载文件失败" + e.getMessage());
            response.getWriter().println(JSON.toJSONString(map));
        }
    }

}